/**
 * Copyright 2010 Butin Kirill <kiryaka@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package view.flexupload.minimalistic.model
{
	import flash.display.GradientType;
	import flash.display.InterpolationMethod;
	import flash.display.Shape;
	import flash.display.SpreadMethod;
	import flash.geom.Matrix;
	
	import mx.containers.Canvas;
	
	import org.flexupload.status.FileStatus;

	public class ThreadProgressBar extends Canvas
	{
		private var partCount:int;
		private var bytesPerBlock:Number;
		
		public var bgColor:uint = 0xF0F0F0;
		public var fillColor:uint = 0xACFFAC;
		public var headColor:uint = 0xFF0000;
		
		private var columnCount:int;
		private var rowCount:int;
		private var columnWidth:int;
		private var rowHeight:int;		
		
		private var threadStart:Array = new Array();
		private var threadEnd:Array = new Array();
		private var threadBytes:Array = new Array(0);
		
		private var gradientBoxMatrix:Matrix = new Matrix();
		
		public function ThreadProgressBar(){
			super();
		}
		
		public function updateStatus(fileStatus:FileStatus):void{
			if (fileStatus.getSize() == fileStatus.getBytesTransfered()) return fillAll();
			if (!bytesPerBlock) bytesPerBlock = fileStatus.getSize()/partCount;
			
			for (var i:int = 0; i < fileStatus.getThreadCount();i++){
				if (i == threadStart.length) addThread();
				
				var bytes:Number = fileStatus.getThreadByIndex(i).getBytesTransfered()-threadBytes[i];
				for(var j:Number = bytes; j > bytesPerBlock; j -= bytesPerBlock){
					if (threadStart[i] == threadEnd[i]){
						fillRect(i, fillColor);
						threadBytes[i] += bytesPerBlock;
						getNewFreeBlock(i);
						continue;
					}
					fillRect(i, fillColor);
					initThread(i, threadStart[i]+1, threadEnd[i], threadBytes[i] + bytesPerBlock);
				}
				fillRect(i, headColor);
			}
		}
		
		public function fillRect(index:int, color:uint):void{
			var part:int = threadStart[index];
			if (part >= partCount) return; // TODO wtf? why it can be more??
			var y:int = Math.floor(part/columnCount);
			var x:int = part - y*columnCount;
			
			drawRect(color, x*columnWidth, y*rowHeight);
		}
		
		public function drawRect(color:uint, x:int, y:int):void{
			//this.graphics.beginFill(color);
			this.graphics.beginGradientFill( GradientType.LINEAR, [0xFFFFFF, 0x666666], [1,1], [0,255], 
				gradientBoxMatrix, SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB, 0);
			this.graphics.drawRect(x, y, columnWidth, rowHeight);
			this.graphics.endFill();
		}		
		
		public function fillAll():void{
			for(var i:int = 0; i < threadStart.length; i++){
				fillRect(i, fillColor);
				while(threadStart[i] <= threadEnd[i]){
					threadStart[i]++;
					fillRect(i, fillColor);
				}
			}
			threadStart = null;
			threadEnd = null;
			threadBytes = null
			
		}
		
		private function getIndexOfMaximalFreeChain():int{
			var index:int = 0;
			var max:int = 0;
			for (var i:int = 0; i < threadStart.length;i++){
				var len:int = threadEnd[i] - threadStart[i];
				if (len > max){
					max = len;
					index = i;
				}
			}
			return index;
		}
		
		private function getNewFreeBlock(cur:int):void{
			var index:int = getIndexOfMaximalFreeChain();
			var max:int = threadEnd[index] - threadStart[index];
			var start:int = max/2 + threadStart[index];
			
			initThread(cur, start, threadEnd[index], threadBytes[cur]);
			
			threadEnd[index] = start-1;			
		}
		private function addThread():void{
			var index:int = getIndexOfMaximalFreeChain();
			var max:int = threadEnd[index] - threadStart[index];
			var start:int = max/2 + threadStart[index]
			
			createThread(start, threadEnd[index], 0.0);	
			threadEnd[index] = start-1;
		}
		
		private function initThread(index:int, start:int, end:int, bytes:Number):void{
			threadStart[index] = start;
			threadEnd[index] = end;
			threadBytes[index] = bytes;
		}
		
		private function createThread(start:int, end:int, bytes:Number):void{
			threadStart.push(start);
			threadEnd.push(end);
			threadBytes.push(bytes);
		}
		
		public function init(columns:int, rows:int, columnWidth:int, rowHeight:int):void{
			this.partCount = columns*rows;
			this.columnCount=columns;
			this.rowCount=rows;
			this.columnWidth = columnWidth;
			this.rowHeight = rowHeight;
			this.width = columns*columnWidth+2;
			this.height=rowHeight*rows + 2;
			createThread(0, partCount-1, 0.0);
			
			gradientBoxMatrix.createGradientBox(200, 20, Math.PI/2, 0, 0);
			this.graphics.beginGradientFill( GradientType.LINEAR, [0xFFFFFF, 0x999999], [0,1], [0,255], 
				gradientBoxMatrix, SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB, 0);
			this.graphics.drawRect(0, 0, columnWidth*width, rows*rowHeight);
			this.graphics.endFill();
			
			this.graphics.beginGradientFill( GradientType.LINEAR, [0xFFFFFF, 0x000000], [1,1], [0,255],
				gradientBoxMatrix, SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB, 0);			
		}
	}
}